package tests;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.HashMap;

import jeconbond.automata.INode;
import jeconbond.automata.IOutcome;
import jeconbond.automata.Outcome;
import jeconbond.automata.Node;
import jeconbond.automata.behaviour.ListPlanWalker;
import jeconbond.economic.agent.AutomataAgent;
import jeconbond.economic.agent.IEconomicAgent;
import jeconbond.economic.agent.service.IPreActServiceAgent;
import jeconbond.economic.market.strategy.*;
import jeconbond.economic.resources.ResourceType;
import jeconbond.economic.resources.IResourceType;
import jeconbond.economic.resources.ResourceBasket;
import jeconbond.economic.resources.ResourceStorrage;
import jeconbond.economic.resources.ResourceUtils;
import jeconbond.economic.systems.market.*;
import jeconbond.economic.IEconomicEngine;
import jeconbond.economic.EconomicEngine;
import jeconbond.experiment.market.equilibrium.process.MooreExperimentContext;
import jeconbond.experiment.market.equilibrium.MooreExperimentSettings;
import junit.framework.Assert;
import junit.framework.TestCase;

public class TestMarketEconomicsystem extends TestCase {
	public void testSellProcessing() {
		IResourceType wood = new ResourceType("Wood");
		ResourceStorrage sellerStorrage = new ResourceStorrage();
		ResourceStorrage buyerStorrage = new ResourceStorrage();
		IMoneyAccount sellerAccount = new MoneyAccount();
		IMoneyAccount buyerAccount = new MoneyAccount();
		ISellProposition sellProposition = new SellProposition(
				wood,
				2.0,
				sellerStorrage,
				sellerAccount
		);
		buyerAccount.changeBalance(2.1);
		sellerStorrage.storeResource(wood, 1.2);
		IMarketResourceRedestributionSystem mrrs = new DefaultMarketResourceRedestributionSystem();
		ResourceUtils.processDeal(
				sellProposition, 
				1.0, 
				buyerAccount, 
				buyerStorrage,
				mrrs
		);
		
		Assert.assertEquals(2.0, sellerAccount.getBalance());
		Assert.assertEquals(2.1 - 2.0, buyerAccount.getBalance());
		Assert.assertEquals(1.2 - 1.0, sellerStorrage.howMuchWasStored(wood));
		Assert.assertEquals(1.0, buyerStorrage.howMuchWasStored(wood));
	}
	
	public void testProductNecessityMerchant() {
		IResourceType wood = new ResourceType("Wood");
		IResourceType oil = new ResourceType("Oil");
		IMarketResourceRedestributionSystem mrrs = 
			new DefaultMarketResourceRedestributionSystem();
		ResourceStorrage woodStorr = new ResourceStorrage();
		ResourceStorrage inputStorr = new ResourceStorrage();
		ResourceStorrage destStorr = new ResourceStorrage();
		woodStorr.storeResource(wood, 1.5);
		IMoneyAccount money = new MoneyAccount();
		money.changeBalance(1.0);
		ObserveCostsProfitMerchantDecorator observeCPMerch = new ObserveCostsProfitMerchantDecorator(
				inputStorr,
				destStorr,
				new tests.fakes.WoodIsNecessary(wood),
				mrrs,
				money,
				1,
				1.0
		);
		Assert.assertTrue(observeCPMerch instanceof IPreActServiceAgent);
		IMoneyAccount wpMoney = new MoneyAccount();
		mrrs.registerProposition(
				new SellProposition(
						wood,
						1.0,
						woodStorr,
						wpMoney
				)
		);
		observeCPMerch.processPreProduceStage();
		observeCPMerch.processPurchase();
		Assert.assertEquals(0.5, woodStorr.howMuchWasStored(wood));
		Assert.assertEquals(1.0, inputStorr.howMuchWasStored(wood));
		observeCPMerch.processProduceStage();
		destStorr.storeResource(oil, 1.0);
		Assert.assertEquals(0, mrrs.getSellPropositionsList(oil).length);
		observeCPMerch.processPostProduceStage();
		observeCPMerch.processSelling();
		Assert.assertEquals(1, mrrs.getSellPropositionsList(oil).length);
	}
	
	public void testEasyMarketWorkFlow() {
		IResourceType wood = new ResourceType("Wood");
		IResourceType trash = new ResourceType("Trash");
		INode root = new Node("root");
		IOutcome out = new Outcome(root, new ResourceBasket(wood, 1.0));
		out.setProducedResources(new ResourceBasket(trash, 3.0));
		root.getOutcomes().add(out);
		AutomataAgent agent = new AutomataAgent(root);
		List<IOutcome> plan = new ArrayList<IOutcome>();
		plan.add(out);
		ListPlanWalker listPlWl = new ListPlanWalker(
				plan,
				agent.getInputRS(),
				agent.getOutputRS()
		);
		IMarketResourceRedestributionSystem mrrs = 
			new DefaultMarketResourceRedestributionSystem();
		IMoneyAccount agentMoney = new MoneyAccount();
		agentMoney.changeBalance(100.0);
		double agentGreedPercent = 1.1;
		ObserveCostsProfitMerchantDecorator observeCPMerch = new ObserveCostsProfitMerchantDecorator(
				agent,
				new tests.fakes.WoodIsNecessary(wood),
				mrrs,
				agentMoney,
				10,
				1.0
		);
		((ProductNecessityMerchant)observeCPMerch.getSubmerchant())
			.setPerformPriceStrategy(
					new MultiplyPerformPriceStrategy(observeCPMerch, agentGreedPercent)
			);
		IEconomicAgent agentSpy = observeCPMerch;
		agent.setMerchant(observeCPMerch);
		agent.setWalkStrategy(listPlWl);
		
		ResourceStorrage woodStorr = new ResourceStorrage();
		woodStorr.storeResource(wood, 1.5);
		IMoneyAccount woodMoney = new MoneyAccount();
		mrrs.registerProposition(
				new SellProposition(wood, 2.0, woodStorr, woodMoney)
		);
		
		agentSpy.processPreProduceStage();
		agent.processPreProduceStage();
		Assert.assertEquals(1.0, agent.getInputRS().howMuchWasStored(wood));
		Assert.assertEquals(98.0, agentMoney.getBalance());
		Assert.assertEquals(2.0, woodMoney.getBalance());
		
		agentSpy.processProduceStage();
		agent.processProduceStage();
		Assert.assertEquals(3.0, agent.getOutputRS().howMuchWasStored(trash));
		Assert.assertEquals(0, mrrs.getSellPropositionsList(trash).length);
		
		agentSpy.processPostProduceStage();
		agent.processPostProduceStage();
		Assert.assertEquals(1, mrrs.getSellPropositionsList(trash).length);
		ISellProposition prop = mrrs.getSellPropositionsList(trash)[0];
		Assert.assertEquals((2.0 / 3.0) * agentGreedPercent, prop.getPrice());
	}
	
	public void testEcSysEvents() {
		IMarketResourceRedestributionSystem mrrs = 
			new DefaultMarketResourceRedestributionSystem();
		final boolean[] listenerWasCalled_ptr = new boolean[] { false };
		mrrs.addSellsListener(
				new ISellsListener() {
					@Override
					public void onSellPerformed(
							ISellProposition proposition,
							double value,
							IMoneyAccount buyerAccount
					) {
						listenerWasCalled_ptr[0] = true;
					}
				}
		);
		IResourceType resource = new ResourceType("r0"); 
		ResourceStorrage storr = new ResourceStorrage();
		storr.storeResource(resource, 100.0);
		IMoneyAccount money = new MoneyAccount();
		money.changeBalance(100.0);
		ResourceUtils.processDeal(
				new SellProposition(
						resource, 
						1.0, 
						storr, 
						money
				), 
				1.0, 
				money, 
				storr, 
				mrrs
		);
		Assert.assertEquals(true, listenerWasCalled_ptr[0]);
	}

	public void testConfiguringPerformPriceStrategy() {
		IResourceType wood = new ResourceType("Wood");
		Map<IResourceType, double[]> concurrentPrices = new HashMap<IResourceType,double[]>();
		IPerformPriceStrategy baseStrat = new IPerformPriceStrategy() {
				public double performPrice(IResourceType resourceType) {
					return 100.0;
				}
			};
		IPerformPriceStrategy resultStrat = ResourceUtils.newConcurGreedPriceStrat(
				baseStrat,
				1.1,
				0.3,
				concurrentPrices
		);
		Assert.assertTrue(resultStrat instanceof ConcurrentBasedPerformPriceStrategy);
		double price = resultStrat.performPrice(wood);
		Assert.assertEquals(100.0 * 1.1, price);
		concurrentPrices.put(wood, new double[] { 200.0 });
		price = resultStrat.performPrice(wood);
		Assert.assertEquals((100.0 * 1.1) * 0.7 + 200.0 * 0.3, price);

		resultStrat = ResourceUtils.newConcurGreedPriceStrat(
				baseStrat,
				1.1,
				-2.0,
				concurrentPrices
		);
		Assert.assertTrue(resultStrat instanceof MultiplyPerformPriceStrategy);
		price = resultStrat.performPrice(wood);
		Assert.assertEquals(100.0 * 1.1, price);
	}

	public void testAvaluateMarketPriceAgent() {
		IEconomicEngine engine = new EconomicEngine();
		MooreExperimentSettings msett = new MooreExperimentSettings();
		msett.rrsFactory = new DefaultMarketRRSFactory();
		msett.resources = ResourceUtils.generateResources("r", 1);
		MooreExperimentContext context = new MooreExperimentContext(msett);
		Map<IResourceType,double[]> prMap =
				context.getConcurrentPricesMap();
		engine.addAgents(context.getServiceAgents());
		IMarketResourceRedestributionSystem rrs = context.getRRS();
		engine.processSteps(100, rrs);

		Assert.assertEquals(null, prMap.get(msett.resources[0]));

		ResourceStorrage storr = new ResourceStorrage();
		storr.storeResource(msett.resources[0], 10.0);
		rrs.registerProposition(
				new SellProposition(msett.resources[0], 100.0, storr, null)
		);
		engine.processSteps(1, rrs);
		Assert.assertEquals(100.0, prMap.get(msett.resources[0])[0]);
		engine.processSteps(10, rrs);
		Assert.assertEquals(100.0, prMap.get(msett.resources[0])[0]);

		rrs.registerProposition(
				new SellProposition(msett.resources[0], 100.0, storr, null)
		);
		rrs.registerProposition(
				new SellProposition(msett.resources[0], 200.0, storr, null)
		);
		engine.processSteps(1, rrs);
		Assert.assertEquals(125.0, prMap.get(msett.resources[0])[0]);
		for (int i = 0; i < 100; i++) {
			rrs.registerProposition(
					new SellProposition(msett.resources[0], 100.0, storr, null)
			);
			rrs.registerProposition(
					new SellProposition(msett.resources[0], 200.0, storr, null)
			);
			engine.processSteps(1, rrs);
		}
		Assert.assertEquals(150.0, prMap.get(msett.resources[0])[0]);
	}
}